﻿using ImageCropper.Util;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace ImageCropper.Helpers
{
    public static class ImageHelper
    {
        public static void GetWidthAndHeight(Stream imageStream, out int width, out int height)
        {
            Image img = Image.FromStream(imageStream);

            width = img.Width;
            height = img.Height;
        }

        public static Stream Compress(int quality, Stream imageStream, string outputPath)
        {
            Stream compressedStream = Compress(quality, imageStream);

            Save(compressedStream, outputPath);

            return compressedStream;
        }

        public static MemoryStream GetStreamFromFile(string absolutePath)
        {
            System.Drawing.Image image = System.Drawing.Image.FromFile(absolutePath);

            MemoryStream stream = new MemoryStream();

            image.Save(stream, ImageFormat.Jpeg);

            image.Dispose();

            return stream;
        }

        public static Stream Compress(int quality, Stream imageStream)
        {
            EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality);
            ImageCodecInfo codec = GetEncoderInfo("image/jpeg");
            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;

            MemoryStream compressedStream = new MemoryStream();

            Image img = Image.FromStream(imageStream);
            img.Save(compressedStream, codec, encoderParams);
            img.Dispose();

            return compressedStream;
        }

        public static MemoryStream Crop(System.Drawing.Image OriginalImage, int Width, int Height, int X, int Y)
        {
            try
            {
                using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Width, Height))
                {
                    bmp.SetResolution(OriginalImage.HorizontalResolution, OriginalImage.VerticalResolution);
                    using (System.Drawing.Graphics Graphic = System.Drawing.Graphics.FromImage(bmp))
                    {
                        Graphic.SmoothingMode = SmoothingMode.AntiAlias;
                        Graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        Graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
                        Graphic.DrawImage(OriginalImage, new System.Drawing.Rectangle(0, 0, Width, Height), X, Y, Width, Height, System.Drawing.GraphicsUnit.Pixel);
                        MemoryStream ms = new MemoryStream();
                        bmp.Save(ms, OriginalImage.RawFormat);
                        return ms;
                    }
                }
            }
            catch (Exception Ex)
            {
                throw (Ex);
            }
        }

        public static Stream Resize(int width, Stream imageStream, bool highQuality, string outputPath)
        {
            Stream resizedStream = Resize(width, imageStream, highQuality);

            Save(resizedStream, outputPath);

            return resizedStream;
        }

        public static Stream Resize(int width, Stream imageStream, bool highQuality)
        {
            if (highQuality)
            {
                return Resize(width, imageStream, CompositingQuality.HighQuality, SmoothingMode.HighQuality, InterpolationMode.HighQualityBicubic);
            }
            else
            {
                return Resize(width, imageStream, CompositingQuality.HighSpeed, SmoothingMode.HighSpeed, InterpolationMode.Low);
            }
        }

        public static Stream Resize(int width, Stream imageStream, CompositingQuality compositingQuality, SmoothingMode smoothingMode, InterpolationMode interpolationMode)
        {
            Image image = Image.FromStream(imageStream);

            int thumbnailSize = width;
            int newWidth, newHeight;

            if (image.Width > image.Height)
            {
                newWidth = thumbnailSize;
                newHeight = image.Height * thumbnailSize / image.Width;
            }
            else
            {
                newWidth = image.Width * thumbnailSize / image.Height;
                newHeight = thumbnailSize;
            }

            Bitmap bitmap = new Bitmap(newWidth, newHeight);

            Graphics graphic = Graphics.FromImage(bitmap);

            graphic.CompositingQuality = compositingQuality;
            graphic.SmoothingMode = smoothingMode;
            graphic.InterpolationMode = interpolationMode;

            Rectangle rectangle = new Rectangle(0, 0, newWidth, newHeight);
            graphic.DrawImage(image, rectangle);

            MemoryStream resizedStream = new MemoryStream();

            ImageFormat format = image.RawFormat;
            bitmap.Save(resizedStream, ImageFormat.Png);

            graphic.Dispose();
            bitmap.Dispose();
            image.Dispose();

            return resizedStream;
        }

        public static void Save(Stream imageStream, string outputPath)
        {
            Image img = Image.FromStream(imageStream);

            img.Save(outputPath);

            img.Dispose();
        }

        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

            for (int i = 0; i < codecs.Length; i++)
            {
                if (codecs[i].MimeType == mimeType)
                {
                    return codecs[i];
                }
            }

            return null;
        }

        public static bool ResizeImage(System.Drawing.Image sourceImage, string targetFile, int maxWidth, int maxHeight, bool preserverAspectRatio, int quality)
        {
            // If 0 is passed in any of the max sizes it means that that size must be ignored,
            // so the original image size is used.
            maxWidth = maxWidth == 0 ? sourceImage.Width : maxWidth;
            maxHeight = maxHeight == 0 ? sourceImage.Height : maxHeight;

            Size oSize;
            if (preserverAspectRatio)
            {
                // Gets the best size for aspect ratio resampling
                oSize = GetAspectRatioSize(maxWidth, maxHeight, sourceImage.Width, sourceImage.Height);
            }
            else
                oSize = new Size(maxWidth, maxHeight);

            System.Drawing.Image oResampled;

            if (sourceImage.PixelFormat == PixelFormat.Indexed || sourceImage.PixelFormat == PixelFormat.Format1bppIndexed || sourceImage.PixelFormat == PixelFormat.Format4bppIndexed || sourceImage.PixelFormat == PixelFormat.Format8bppIndexed || sourceImage.PixelFormat.ToString() == "8207")
                oResampled = new Bitmap(oSize.Width, oSize.Height, PixelFormat.Format24bppRgb);
            else
                oResampled = new Bitmap(oSize.Width, oSize.Height, sourceImage.PixelFormat);

            // Creates a Graphics for the oResampled image
            Graphics oGraphics = Graphics.FromImage(oResampled);

            // The Rectangle that holds the Resampled image size
            Rectangle oRectangle;

            // High quality resizing
            if (quality > 80)
            {
                oGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

                // If HighQualityBicubic is used, bigger Rectangle is required to remove the white border
                oRectangle = new Rectangle(-1, -1, oSize.Width + 1, oSize.Height + 1);
            }
            else
                oRectangle = new Rectangle(0, 0, oSize.Width, oSize.Height);

            // Place a white background (for transparent images).
            oGraphics.FillRectangle(new SolidBrush(Color.White), oRectangle);

            // Draws over the oResampled image the resampled Image
            oGraphics.DrawImage(sourceImage, oRectangle);

            sourceImage.Dispose();

            String extension = System.IO.Path.GetExtension(targetFile).ToLower();

            if (extension == ".jpg" || extension == ".jpeg")
            {
                ImageCodecInfo oCodec = GetJpgCodec();

                if (oCodec != null)
                {
                    EncoderParameters aCodecParams = new EncoderParameters(1);
                    aCodecParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);

                    oResampled.Save(targetFile, oCodec, aCodecParams);
                }
                else
                    oResampled.Save(targetFile);
            }
            else
            {
                switch (extension)
                {
                    case ".gif":
                        try
                        {
                            // Use a proper palette
                            OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
                            using (Bitmap quantized = quantizer.Quantize(oResampled))
                            {
                                quantized.Save(targetFile, System.Drawing.Imaging.ImageFormat.Gif);
                            }
                        }
                        catch (System.Security.SecurityException)
                        {
                            // The calls to Marshal might fail in Medium trust, save the image using the default palette
                            oResampled.Save(targetFile, System.Drawing.Imaging.ImageFormat.Png);
                        }
                        break;

                    case ".png":
                        oResampled.Save(targetFile, System.Drawing.Imaging.ImageFormat.Png);
                        break;

                    case ".bmp":
                        oResampled.Save(targetFile, System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                }
            }
            oGraphics.Dispose();
            oResampled.Dispose();

            return true;
        }

        private static ImageCodecInfo GetJpgCodec()
        {
            ImageCodecInfo[] aCodecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo oCodec = null;

            for (int i = 0; i < aCodecs.Length; i++)
            {
                if (aCodecs[i].MimeType.Equals("image/jpeg"))
                {
                    oCodec = aCodecs[i];
                    break;
                }
            }

            return oCodec;
        }

        private static Size GetAspectRatioSize(int maxWidth, int maxHeight, int actualWidth, int actualHeight)
        {
            // Creates the Size object to be returned
            Size oSize = new System.Drawing.Size(maxWidth, maxHeight);

            // Calculates the X and Y resize factors
            float iFactorX = (float)maxWidth / (float)actualWidth;
            float iFactorY = (float)maxHeight / (float)actualHeight;

            // If some dimension have to be scaled
            if (iFactorX != 1 || iFactorY != 1)
            {
                // Uses the lower Factor to scale the opposite size
                if (iFactorX < iFactorY) { oSize.Height = (int)Math.Round((float)actualHeight * iFactorX); }
                else if (iFactorX > iFactorY) { oSize.Width = (int)Math.Round((float)actualWidth * iFactorY); }
            }

            if (oSize.Height <= 0) oSize.Height = 1;
            if (oSize.Width <= 0) oSize.Width = 1;

            // Returns the Size
            return oSize;
        }
    }
}